package com.zhidian.edm.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zhidian.edm.entity.SysMenuPO;
import com.zhidian.edm.entity.SysRoleMenuPO;
import com.zhidian.edm.entity.SysUserRolePO;
import com.zhidian.edm.mapper.SysMenuMapper;
import com.zhidian.edm.mapper.SysRoleMenuMapper;
import com.zhidian.edm.mapper.SysUserRoleMapper;
import com.zhidian.edm.param.AddMenuParam;
import com.zhidian.edm.param.UpdateMenuParam;
import com.zhidian.edm.service.ISysMenuService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zhidian.edm.util.BeanConvertUtil;
import com.zhidian.edm.util.JwtUtil;
import com.zhidian.edm.util.ListUtil;
import com.zhidian.edm.util.TreeUtil;
import com.zhidian.edm.vo.MetaVO;
import com.zhidian.edm.vo.RouterVO;
import com.zhidian.edm.vo.SysMenuDetailVO;
import com.zhidian.edm.vo.SysMenuVO;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author sl
 * @since 2025-09-18
 */
@Service
public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenuPO> implements ISysMenuService {

    @Resource
    private SysMenuMapper menuMapper;

    @Resource
    private SysRoleMenuMapper roleMenuMapper;

    @Resource
    private SysUserRoleMapper sysUserRoleMapper;

    @Resource
    private JwtUtil jwtUtil;

    @Override
    public List<RouterVO> getMenuRouters(HttpServletRequest req) {
        List<RouterVO> result = List.of();
        Long userId = jwtUtil.getUserId(req.getHeader("Authorization"));
        List<SysUserRolePO> sysUserRolePOS = sysUserRoleMapper.selectList(new LambdaQueryWrapper<SysUserRolePO>().eq(SysUserRolePO::getUserId, userId));
        if (CollectionUtils.isEmpty(sysUserRolePOS)) {
            return result;
        }
        List<Long> roleIds = ListUtil.toValueList(sysUserRolePOS, SysUserRolePO::getRoleId);

        // 2. 查角色关联的菜单
        List<SysRoleMenuPO> roleMenus = roleMenuMapper.selectList(
                new LambdaQueryWrapper<SysRoleMenuPO>().in(SysRoleMenuPO::getRoleId, roleIds)
        );
        if (CollectionUtils.isEmpty(roleMenus)) {
            return result;
        }
        List<Long> menuIds = roleMenus.stream()
                .map(SysRoleMenuPO::getMenuId)
                .distinct()
                .collect(Collectors.toList());

        // 3. 查询菜单（过滤掉按钮、不可见的）
        List<SysMenuPO> menus = menuMapper.selectList(
                new LambdaQueryWrapper<SysMenuPO>()
                        .in(SysMenuPO::getId, menuIds)
                        .eq(SysMenuPO::getVisible, true)
                        .ne(SysMenuPO::getMenuType, 2) // 过滤按钮
                        .orderByAsc(SysMenuPO::getSort)
        );
        if (CollectionUtils.isEmpty(menus)) {
            return result;
        }
        // 4. 转换成 RouterVO
        List<RouterVO> routers = menus.stream().map(menu -> {
            RouterVO router = new RouterVO();
            router.setName(menu.getRouterName());
            router.setPath(menu.getPath());
            router.setComponent(menu.getComponent());
            router.setHidden(!menu.getVisible()); // visible=true => hidden=false

            MetaVO meta = new MetaVO();
            meta.setTitle(menu.getName());
            meta.setIcon(menu.getIcon());
            meta.setNoCache(menu.getCache() != null && menu.getCache() == 1);
            router.setMeta(meta);

            router.setChildren(new ArrayList<>()); // 预置空 list
            return router;
        }).collect(Collectors.toList());

        return TreeUtil.buildRouterTree(menus, routers);
    }

    /**
     * 查询所有菜单树（包含隐藏菜单）
     */
    @Override
    public List<SysMenuVO> getAllMenuTree() {
        // 1. 查询所有可见菜单（visible=1）
        LambdaQueryWrapper<SysMenuPO> wrapper = new LambdaQueryWrapper<>();
        wrapper.orderByAsc(SysMenuPO::getSort); // 按排序号升序
        List<SysMenuPO> allMenus = menuMapper.selectList(wrapper);
        List<SysMenuVO> sysMenuVOS = BeanConvertUtil.convertList(allMenus, SysMenuVO.class);
        // 2. 构建树形结构（顶级菜单parent_id=0）
         return TreeUtil.buildTree(
                sysMenuVOS,
                SysMenuVO::getId,
                SysMenuVO::getParentId
        );

    }

    /**
     * 查询所有菜单树（不包含隐藏菜单）
     */
    @Override
    public List<SysMenuVO> getAllMenuTreeAndNoVisible() {
        // 1. 查询所有菜单
        LambdaQueryWrapper<SysMenuPO> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(SysMenuPO::getVisible, 1) // 只要可见的
                .orderByAsc(SysMenuPO::getSort);

        List<SysMenuPO> allMenus = menuMapper.selectList(wrapper);

        // 2. 转换成 VO
        List<SysMenuVO> sysMenuVOS = BeanConvertUtil.convertList(allMenus, SysMenuVO.class);

        // 3. 构建树形结构
        return TreeUtil.buildTree(
                sysMenuVOS,
                SysMenuVO::getId,
                SysMenuVO::getParentId
        );
    }

    /**
     * 根据角色ID查询该角色有权访问的菜单树
     */
    @Override
    public List<SysMenuVO> getMenuTreeByRoleId(Long roleId) {
        // 1. 查询角色关联的菜单ID
        LambdaQueryWrapper<SysRoleMenuPO> roleMenuWrapper = new LambdaQueryWrapper<>();
        roleMenuWrapper.eq(SysRoleMenuPO::getRoleId, roleId);
        List<SysRoleMenuPO> roleMenus = roleMenuMapper.selectList(roleMenuWrapper);
        if (CollectionUtils.isEmpty(roleMenus)) {
            return new ArrayList<>(); // 角色无关联菜单，返回空列表
        }

        // 2. 提取菜单ID列表
        List<Long> menuIds = roleMenus.stream()
                .map(SysRoleMenuPO::getMenuId)
                .collect(Collectors.toList());

        // 3. 查询这些菜单中可见的菜单（visible=1）
        LambdaQueryWrapper<SysMenuPO> menuWrapper = new LambdaQueryWrapper<>();
        menuWrapper.in(SysMenuPO::getId, menuIds)
                .eq(SysMenuPO::getVisible, 1)
                .orderByAsc(SysMenuPO::getSort);
        List<SysMenuPO> roleVisibleMenus = menuMapper.selectList(menuWrapper);
        List<SysMenuVO> roleVisibleVOs = BeanConvertUtil.convertList(roleVisibleMenus, SysMenuVO.class);
        // 4. 构建树形结构
        return TreeUtil.buildTree(
                roleVisibleVOs,
                SysMenuVO::getId,
                SysMenuVO::getParentId
        );
    }

    /**
     * 根据当前登录用户的角色查询菜单树（用于前端渲染）
     */
    @Override
    public List<SysMenuVO> getCurrentUserMenuTree(List<Long> roleIds) {
        if (CollectionUtils.isEmpty(roleIds)) {
            return new ArrayList<>();
        }

        // 1. 查询多个角色关联的所有菜单ID（去重）
        LambdaQueryWrapper<SysRoleMenuPO> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(SysRoleMenuPO::getRoleId, roleIds);
        List<SysRoleMenuPO> roleMenus = roleMenuMapper.selectList(wrapper);
        if (CollectionUtils.isEmpty(roleMenus)) {
            return new ArrayList<>();
        }

        List<Long> menuIds = roleMenus.stream()
                .map(SysRoleMenuPO::getMenuId)
                .distinct() // 去重，避免同一菜单被多个角色关联
                .collect(Collectors.toList());

        // 2. 查询可见菜单并构建树形结构
        LambdaQueryWrapper<SysMenuPO> menuWrapper = new LambdaQueryWrapper<>();
        menuWrapper.in(SysMenuPO::getId, menuIds)
                .eq(SysMenuPO::getVisible, 1)
                .orderByAsc(SysMenuPO::getSort);
        List<SysMenuPO> userMenus = menuMapper.selectList(menuWrapper);
        List<SysMenuVO> sysMenuVOS = BeanConvertUtil.convertList(userMenus, SysMenuVO.class);

        return TreeUtil.buildTree(
                sysMenuVOS,
                SysMenuVO::getId,
                SysMenuVO::getParentId
        );
    }

    @Override
    public SysMenuDetailVO getMenuInfoById(Long menuId) {
        return Optional.ofNullable(menuMapper.selectById(menuId))
                .map(menuPO -> BeanConvertUtil.convert(menuPO, SysMenuDetailVO.class))
                .orElse(null);
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteMenuById(Long menuId) {
        List<Long> allIds = new ArrayList<>();
        collectSubMenuIds(menuId, allIds);
        if (!allIds.isEmpty()) {
            menuMapper.deleteByIds(allIds);
            roleMenuMapper.delete(new LambdaQueryWrapper<SysRoleMenuPO>().eq(SysRoleMenuPO::getMenuId, menuId));
        }
    }

    /**
     * 递归收集子菜单 id
     */
    private void collectSubMenuIds(Long menuId, List<Long> ids) {
        // 先加上自己
        ids.add(menuId);

        // 查找子菜单
        List<SysMenuPO> children = menuMapper.selectList(
                new LambdaQueryWrapper<SysMenuPO>().eq(SysMenuPO::getParentId, menuId)
        );

        for (SysMenuPO child : children) {
            collectSubMenuIds(child.getId(), ids);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addMenu(AddMenuParam param) {

        if (param.getParentId() != 0) {
            SysMenuPO parentMenu = getById(param.getParentId());
            if (Objects.isNull(parentMenu)) {
                throw new RuntimeException("父菜单不存在");
            }
        }

        // 2. 校验菜单名称是否重复（同一父菜单下）
        LambdaQueryWrapper<SysMenuPO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(SysMenuPO::getParentId, param.getParentId())
                .eq(SysMenuPO::getName, param.getName());
        if (count(queryWrapper) > 0) {
            throw new RuntimeException("同一父菜单下已存在同名菜单");
        }
        SysMenuPO menuPO = BeanConvertUtil.convert(param, SysMenuPO.class);
        menuPO.setCreatedTime(new Date());
        menuMapper.insert(menuPO);
    }

    @Override
    public void updateMenu(UpdateMenuParam param) {
        // 1. 校验菜单是否存在
        SysMenuPO existingMenu = menuMapper.selectById(param.getId());
        if (Objects.isNull(existingMenu)) {
            throw new RuntimeException("菜单不存在");
        }

        // 2. 校验父菜单是否存在（如果修改了父菜单）
        if (param.getParentId() != null && !param.getParentId().equals(existingMenu.getParentId())) {
            // 排除自己作为父菜单的情况
            if (param.getParentId().equals(param.getId())) {
                throw new RuntimeException("不能将自身设置为父菜单");
            }

            // 校验父菜单是否存在
            SysMenuPO parentMenu = getById(param.getParentId());
            if (parentMenu == null) {
                throw new RuntimeException("父菜单不存在");
            }
        }

        // 3. 校验菜单名称是否重复（同一父菜单下）
        if (param.getName() != null && !param.getName().equals(existingMenu.getName())) {
            Long parentId = param.getParentId() != null ? param.getParentId() : existingMenu.getParentId();

            LambdaQueryWrapper<SysMenuPO> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(SysMenuPO::getParentId, parentId)
                    .eq(SysMenuPO::getName, param.getName())
                    .ne(SysMenuPO::getId, param.getId()); // 排除当前菜单

            if (count(queryWrapper) > 0) {
                throw new RuntimeException("同一父菜单下已存在同名菜单");
            }
        }

        // 4. 复制更新参数到实体
        BeanConvertUtil.copyProperties(param, existingMenu);
        existingMenu.setUpdatedTime(new Date());
        // 6. 保存更新
        menuMapper.updateById(existingMenu);
    }


}
